#  generic_protocol.rb
#
#  This file is part of myvnet.
#
#  Copyright (C) 2008  Filipe David Borba Manana,  fdmanana@gmail.com
#
#  myvnet is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  myvnet is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with myvnet.  If not, see <http://www.gnu.org/licenses/>.


require 'lib/exceptions'


# Abstract module. To be used as a base for any module representing some 
# network protocol.
#
module GenericProtocol
  
  # Represents a generic packet. To be subclassed by any protocol 
  # specific Packet class.
  #
  # *Note*: Abstract class.
  #
  class Packet
    attr_accessor :payload

   
    def initialize(bstream = nil)
      return if bstream.nil?
      
      raise BadArgType, "Expected a String argument or no argument" unless bstream.is_a? String      
    end
    
    
    def initialize_copy(orig)
      if orig.payload.is_a? String or orig.payload.is_a? GenericProtocol::Packet
        @payload = orig.payload.dup
      end
    end
    
    
    #
    # Should match the lowercase version of the name of the enclosing module (as a symbol).
    #
    # *Note*: To be overrided by subclasses.
    #
    @packet_type = :generic_protocol
    
    #
    # Number of the layer, in the TCP/IP protocol stack, the protocol, of the packets represented by
    # by this class, belongs to.
    #
    # *Notes*:
    # * To be overrided by subclasses.
    # * Value must be in the range [1..5]
    #
    @layer = 0
                
    # === Synopsis
    #
    #   GenericProtocol::Packet.packet_type
    # 
    # === Description
    #
    # Returns the name of the protocol, as a lowercase symbol, that this class represents packets of.
    #
    def self.packet_type
      @packet_type
    end
    
    
    # === Synopsis
    #
    #   GenericProtocol::Packet#packet_type
    # 
    # === Description
    #
    # Returns the name of the protocol, as a lowercase symbol, that this instance represents a packet of.
    #
    def packet_type
      self.class.packet_type
    end
    
    
    # === Synopsis
    #
    #   GenericProtocol::Packet.layer
    # 
    # === Description
    #
    # Returns the layer number, in the TCP/IP protocol stack, to which the protocol of packets
    # represented by this class, belongs to.
    #
    def self.layer
      @layer
    end
    
    
    # === Synopsis
    #
    #   GenericProtocol::Packet#layer
    # 
    # === Description
    #
    # Returns the layer number, in the TCP/IP protocol stack, to which the protocol of this
    # packet belongs to.
    #
    def layer
      self.class.layer
    end
    
    
    # === Synopsis
    #
    #   GenericProtocol::Packet#payload?
    #
    # === Return
    #
    # True if the packet's payload is not nil and it's not an empty byte stream. False otherwise.
    #
    def payload?
      return false if @payload.nil?
      return false if @payload.is_a? String and @payload.empty?
      
      true
    end
    
    
    # === Synopsis
    #
    #   GenericProtocol::Packet#build_upper_layers
    #
    # === Return
    #
    # A copy of the receiver packet with the payload transformed into a 
    # GenericProtocol::Packet subclass instance.  If there's no packet 
    # encapsulated by the receiver packet, or the encapsulated packet's 
    # protocol is not recognized by myvnet, it returns the receiver 
    # packet as it is.
    #
    # === Description
    #
    # If the receiver packet's payload is a byte stream (String), converts 
    # it into a GenericProtocol::Packet subclass instance. This operation 
    # is done recursively on each generated Packet instance's payload until 
    # either there are no more packets encapsulated or the encapsulated 
    # packet's protocol is not recognized by myvnet.
    #
    # *Notes*:
    # * Does not modify the packet - returns a modified copy of it. 
    # * Usually doesn't need to be overriden by subclasses, as long as they override <tt>dup</tt> 
    #   and <tt>build_upper_layers!</tt> methods.
    #
    def build_upper_layers
      packet = self.dup
      packet.build_upper_layers!
    end
  

    # === Synopsis
    #
    #   GenericProtocol::Packet#build_upper_layers!
    #
    # === Return
    #
    # Returns itself.
    #
    # === Description
    #
    # If the receiver packet's payload is a byte stream (String), converts it 
    # into a GenericProtocol::Packet subclass instance. This operation is done 
    # recursively on each generated Packet instance's payload until either 
    # there are no more packets encapsulated or the encapsulated packet's 
    # protocol is not recognized by myvnet.
    #
    # *Notes*:
    # * It modifies the receiver packet. 
    # * To be overrided by subclasses.
    #
    def build_upper_layers!
      self
    end
    
    
    # === Synopsis
    #
    #   GenericProtocol::Packet#to_bstream
    #
    # === Return
    #
    # A byte stream, in network byte order, representation of the packet.
    #
    # === Description
    #
    # Builds a byte stream (String instance), in network byte order, 
    # representation of the packet.
    #
    # *Note*: To be overrided by subclasses.
    #
    def to_bstream
      raise TypeError, "GenericProtocol::Packet#to_bstream is an abstract method"
    end
    
  end
  
end